1 module hip.windowing.platforms.windows; 2 import hip.windowing.input; 3 import hip.windowing.events; 4 5 6 version(UWP){} 7 else version(Windows) 8 version = WindowsNative; 9 10 version(WindowsNative) 11 { 12 import core.sys.windows.winuser; 13 import core.sys.windows.wingdi; 14 import core.sys.windows.winbase : GetModuleHandle, 15 GetLastError, 16 FormatMessage, 17 FORMAT_MESSAGE_ALLOCATE_BUFFER, 18 FORMAT_MESSAGE_FROM_SYSTEM, 19 LocalFree; 20 import core.sys.windows.windef; 21 22 alias HWND = void*; 23 alias HINSTANCE = void*; 24 package const(wchar)* winClassName = "HipremeEngine"; 25 package __gshared HDC hdc; 26 package HGLRC glContext; 27 28 pragma(lib, "opengl32"); 29 pragma(lib, "gdi32"); 30 pragma(lib, "user32"); //Can't import that to UWP 31 pragma(lib, "kernel32");//Can't import that to UWP 32 nothrow ushort LOWORD(ulong l) {return cast(ushort) l;} 33 nothrow ushort HIWORD(ulong l) {return cast(ushort) (l >>> 16);} 34 nothrow @system int GET_X_LPARAM(LPARAM lp){return cast(int)cast(short)LOWORD(lp);} 35 nothrow @system int GET_Y_LPARAM(LPARAM lp){return cast(int)cast(short)HIWORD(lp);} 36 nothrow @system uint GET_XBUTTON_WPARAM(WPARAM wp){ return cast(uint)HIWORD(wp);} 37 38 39 package extern(Windows) LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) nothrow @system 40 { 41 switch(msg) 42 { 43 case WM_CLOSE: 44 if(onWindowClosed != null) 45 onWindowClosed(); 46 DestroyWindow(hwnd); 47 break; 48 case WM_DESTROY: 49 PostQuitMessage(0); 50 ReleaseDC(hwnd, hdc); 51 break; 52 case WM_CHAR: 53 case WM_SYSCHAR: 54 if(onTextInput != null) 55 onTextInput(cast(wchar)wParam); 56 break; 57 case WM_SYSKEYDOWN: 58 case WM_KEYDOWN: 59 if(onKeyDown != null) 60 onKeyDown(cast(uint)wParam); 61 break; 62 case WM_SYSKEYUP: 63 case WM_KEYUP: 64 if(onKeyUp != null) 65 onKeyUp(cast(uint)wParam); 66 break; 67 case WM_SIZE: //Resize 68 { 69 UINT width = LOWORD(lParam); 70 UINT height = HIWORD(lParam); 71 if(onWindowResize != null) 72 onWindowResize(width, height); 73 break; 74 } 75 case WM_MOVE: 76 { 77 break; 78 } 79 case WM_MOUSEMOVE: 80 { 81 if(onMouseMove != null) 82 onMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 83 break; 84 } 85 case WM_LBUTTONDOWN: 86 if(onMouseDown != null) 87 onMouseDown(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 88 break; 89 case WM_MBUTTONDOWN: 90 if(onMouseDown != null) 91 onMouseDown(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 92 break; 93 case WM_RBUTTONDOWN: 94 if(onMouseDown != null) 95 onMouseDown(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 96 break; 97 case WM_XBUTTONDOWN: 98 if(onMouseDown != null) 99 onMouseDown( 100 GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2, 101 GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) 102 ); 103 break; 104 case WM_LBUTTONUP: 105 if(onMouseUp != null) 106 onMouseUp(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 107 break; 108 case WM_MBUTTONUP: 109 if(onMouseUp != null) 110 onMouseUp(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 111 break; 112 case WM_RBUTTONUP: 113 if(onMouseUp != null) 114 onMouseUp(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 115 break; 116 case WM_XBUTTONUP: 117 if(onMouseUp != null) 118 onMouseUp( 119 GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2, 120 GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) 121 ); 122 break; 123 case WM_MOUSEWHEEL: 124 if(onMouseWheel != null) 125 onMouseWheel( 126 0, cast(int)GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA 127 ); 128 break; 129 default: 130 return DefWindowProc(hwnd, msg, wParam, lParam); 131 } 132 return 0; 133 } 134 135 extern(Windows) nothrow @nogc HGLRC wglCreateContextAttribs(HDC, DWORD, HWND); 136 alias wglChoosePixelFormatARBProc = extern(Windows) nothrow @nogc BOOL function( 137 HDC hdc, const(int)* piAttribFList, const float* pfAttribIList, uint nMaxFormats, 138 int* piFormats, uint* nNumFormats); 139 140 alias wglCreateContextAttribsARBProc = extern(Windows) nothrow @nogc HGLRC function( 141 HDC hdc, HGLRC hShareContext,const int* attribList 142 ); 143 144 alias wglSwapIntervalEXTProc = extern(Windows) nothrow @nogc int function(int interval); 145 146 147 wglSwapIntervalEXTProc wglSwapIntervalEXT; 148 wglChoosePixelFormatARBProc wglChoosePixelFormatARB; 149 wglCreateContextAttribsARBProc wglCreateContextAttribsARB; 150 extern(Windows) nothrow @nogc void* wglGetProcAddress(const(char)* funcName); 151 152 153 extern(Windows) nothrow @nogc bool initializeOpenGL(ref HWND hwnd, int majorVersion, int minorVersion) 154 { 155 PIXELFORMATDESCRIPTOR pfd = 156 { 157 PIXELFORMATDESCRIPTOR.sizeof, 158 1, 159 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER , // Flags 160 PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. 161 32, // Colordepth of the framebuffer. 162 0, 0, 0, 0, 0, 0, 163 0, 164 0, 165 0, 166 0, 0, 0, 0, 167 24, // Number of bits for the depthbuffer 168 8, // Number of bits for the stencilbuffer 169 0, // Number of Aux buffers in the framebuffer. 170 PFD_MAIN_PLANE, 171 0, 172 0, 0, 0 173 }; 174 int formatIndex = ChoosePixelFormat(hdc, &pfd); 175 if(formatIndex == 0) 176 { 177 MessageBox(NULL, "Could not choose pixel format!", "Error!", MB_ICONERROR | MB_OK); 178 return false; 179 } 180 if(!SetPixelFormat(hdc, formatIndex, &pfd)) 181 { 182 MessageBox(NULL, "Could not set pixel format!", "Error!", MB_ICONERROR | MB_OK); 183 return false; 184 } 185 glContext = wglCreateContext(hdc); 186 if(glContext is null) 187 { 188 MessageBox(NULL, "Could not create OpenGL Context", "Error!", MB_ICONERROR | MB_OK); 189 return false; 190 } 191 if(!wglMakeCurrent(hdc, glContext)) 192 { 193 MessageBox(NULL, "Coult not set OpenGL Context", "Error!", MB_ICONERROR | MB_OK); 194 return false; 195 } 196 if(majorVersion < 3 && minorVersion < 3) //This is not actually tested 197 { 198 if(!GetPixelFormat(hdc)) 199 { 200 MessageBox(NULL, "Could not get window pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK); 201 return false; 202 } 203 if(!DescribePixelFormat(hdc, formatIndex, pfd.sizeof, &pfd)) 204 { 205 MessageBox(NULL, "Could not get describe pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK); 206 return false; 207 } 208 if((pfd.dwFlags & PFD_SUPPORT_OPENGL) != PFD_SUPPORT_OPENGL) 209 { 210 MessageBox(NULL, "PixelFormatDescriptor does not support opengl!", "Error!", MB_ICONEXCLAMATION | MB_OK); 211 return false; 212 } 213 return true; 214 } 215 else 216 return initializeModernOpenGL(hwnd, majorVersion, minorVersion); 217 } 218 219 package bool initializeModernOpenGL(ref HWND hwnd, int majorVersion, int minorVersion) nothrow @nogc 220 { 221 //Load Function Pointers 222 wglChoosePixelFormatARB = cast(wglChoosePixelFormatARBProc)wglGetProcAddress("wglChoosePixelFormatARB"); 223 if(wglChoosePixelFormatARB is null) 224 { 225 MessageBox(NULL, "Could not load wglChoosePixelFormatARB", "Error", MB_ICONERROR | MB_OK); 226 return false; 227 } 228 wglCreateContextAttribsARB = cast(wglCreateContextAttribsARBProc)wglGetProcAddress("wglCreateContextAttribsARB"); 229 if(wglCreateContextAttribsARB is null) 230 { 231 MessageBox(NULL, "Could not load wglCreateContextAttribsARB", "Error", MB_ICONERROR | MB_OK); 232 return false; 233 } 234 wglSwapIntervalEXT = cast(wglSwapIntervalEXTProc)wglGetProcAddress("wglSwapIntervalEXT"); 235 if(wglSwapIntervalEXT is null) 236 { 237 MessageBox(NULL, "Could not load wglSwapIntervalEXT", "Error", MB_ICONERROR | MB_OK); 238 return false; 239 } 240 //Now, for the modern OpenGL 241 const int[19] attribList = 242 [ 243 WGL_DRAW_TO_WINDOW_ARB, true, 244 WGL_SUPPORT_OPENGL_ARB, true, 245 WGL_DOUBLE_BUFFER_ARB, true, 246 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, 247 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, 248 WGL_COLOR_BITS_ARB, 32, 249 WGL_DEPTH_BITS_ARB, 24, 250 WGL_STENCIL_BITS_ARB, 8, 251 WGL_ALPHA_BITS_ARB, 8, 252 0, // End 253 ]; 254 255 int pixelFormat; 256 uint numFormats; 257 258 if(!wglChoosePixelFormatARB(hdc, attribList.ptr, null, 1, &pixelFormat, &numFormats) || numFormats == 0) 259 { 260 MessageBox(NULL, "Could notchoose pixel format", "Error", MB_ICONERROR | MB_OK); 261 return false; 262 } 263 264 auto oldHwnd = hwnd; 265 HDC oldHDC = hdc; 266 HGLRC oldGLContext = glContext; 267 268 RECT rBorders; 269 GetWindowRect(hwnd, &rBorders); 270 RECT rNoBorders; 271 GetClientRect(hwnd, &rNoBorders); 272 RECT r; 273 r.left = rBorders.left*2 - rNoBorders.left; 274 r.right = rBorders.right*2 - rNoBorders.right; 275 r.top = rBorders.top*2 - rNoBorders.top; 276 r.bottom = rBorders.bottom*2 - rNoBorders.bottom; 277 //Create 278 hwnd = createWindow(r.right - r.left, r.bottom - r.top); 279 280 hdc = GetDC(hwnd); 281 282 PIXELFORMATDESCRIPTOR newPFD; 283 DescribePixelFormat(hdc, pixelFormat, newPFD.sizeof, &newPFD); 284 SetPixelFormat(hdc, pixelFormat, &newPFD); 285 286 int[7] contextAttribs = 287 [ 288 WGL_CONTEXT_MAJOR_VERSION_ARB, majorVersion, 289 WGL_CONTEXT_MINOR_VERSION_ARB, minorVersion, 290 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 291 0 292 ]; 293 glContext = wglCreateContextAttribsARB(hdc, null, contextAttribs.ptr); 294 if(glContext is null) 295 { 296 MessageBox(null, "Could not create Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK); 297 return false; 298 } 299 wglMakeCurrent(null, null); 300 wglDeleteContext(oldGLContext); 301 ReleaseDC(oldHwnd, oldHDC); 302 DestroyWindow(oldHwnd); 303 if(!wglMakeCurrent(hdc, glContext)) 304 { 305 MessageBox(null, "Could not set Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK); 306 return false; 307 } 308 return true; 309 310 } 311 312 bool registerClass() 313 { 314 HINSTANCE hInstance = GetModuleHandle(null); 315 WNDCLASS wc; 316 //Register window class 317 318 wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; 319 wc.lpfnWndProc = &WndProc; 320 wc.cbClsExtra = 0; 321 wc.cbWndExtra = 0; 322 wc.hInstance = hInstance; //Application handle 323 wc.hIcon = LoadIcon(null, IDI_APPLICATION); //Big icon 324 wc.hCursor = LoadCursor(null, IDC_ARROW); //Cursor 325 wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW); //Background brush 326 wc.lpszMenuName = null; //Name of menu resource 327 wc.lpszClassName = winClassName; //Name to identify this class of windows 328 329 if(!RegisterClass(cast(const(WNDCLASSW)*)&wc)) 330 { 331 uint err = GetLastError(); 332 wchar* buffer; 333 uint size = FormatMessage( 334 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 335 cast(void*)null,err, 0, cast(LPWSTR)&buffer, 0, null); 336 wstring str = cast(wstring)buffer[0..size]; 337 MessageBox(NULL, ("Window Registration Failed with message: "~str).ptr, "Error!", MB_ICONEXCLAMATION | MB_OK); 338 LocalFree(buffer); 339 return false; 340 } 341 return true; 342 } 343 package HWND createWindow(int width, int height) @nogc nothrow 344 { 345 return CreateWindowEx( 346 0, 347 winClassName, 348 winClassName, //Title 349 WS_OVERLAPPEDWINDOW | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE, 350 CW_USEDEFAULT, CW_USEDEFAULT, 351 width, height, HWND_DESKTOP, null, GetModuleHandle(null), null 352 ); 353 } 354 355 extern(Windows) LRESULT openWindow(out HWND hwnd, ref int width, ref int height) 356 { 357 static bool registeredClass = false; 358 if(!registeredClass) 359 { 360 if(!registerClass()) 361 return 0; 362 } 363 hwnd = createWindow(width, height); 364 if(hwnd == null) 365 { 366 MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); 367 return 0; 368 } 369 hdc = GetDC(hwnd); 370 371 return 1; 372 } 373 374 void show(HWND hwnd) 375 { 376 ShowWindow(hwnd, SW_NORMAL); 377 UpdateWindow(hwnd); 378 } 379 380 void poll() 381 { 382 MSG msg; 383 while(PeekMessage(&msg, cast(void*)null, 0,0, PM_REMOVE)) //GetMessage may be a lot better 384 { 385 TranslateMessage(&msg); 386 DispatchMessage(&msg); 387 } 388 } 389 390 void swapBuffer() 391 { 392 SwapBuffers(hdc); 393 } 394 395 int[2] getWindowSize(HWND hwnd) 396 { 397 RECT rect; 398 GetClientRect(hwnd, &rect); 399 return [rect.right - rect.left, rect.bottom - rect.top]; 400 } 401 void setWindowName(HWND hwnd, string name) 402 { 403 SetWindowTextA(hwnd, name.ptr); 404 } 405 int[2] getWindowBorder(HWND hwnd) 406 { 407 RECT rBorders; 408 GetWindowRect(hwnd, &rBorders); 409 RECT rNoBorders; 410 GetClientRect(hwnd, &rNoBorders); 411 RECT r; 412 r.left = rBorders.left - rNoBorders.left; 413 r.right = rBorders.right - rNoBorders.right; 414 r.top = rBorders.top - rNoBorders.top; 415 r.bottom = rBorders.bottom - rNoBorders.bottom; 416 417 return[r.right - r.left, r.bottom - r.top]; 418 } 419 420 void setWindowSize(HWND hwnd, int width, int height) 421 { 422 int[2] borders = getWindowBorder(hwnd); 423 SetWindowPos(hwnd, null, 0, 0, width + borders[0], height+borders[1], SWP_NOMOVE); 424 } 425 426 void setVsyncActive(bool active) @nogc nothrow @system 427 { 428 if(wglSwapIntervalEXT !is null) 429 { 430 wglSwapIntervalEXT(cast(int)active); 431 } 432 } 433 434 extern(Windows) bool destroy_GL_Context() 435 { 436 if(!wglMakeCurrent(hdc, null)) 437 { 438 MessageBox(NULL, "Could not detach OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK); 439 return false; 440 } 441 if(!wglDeleteContext(glContext)) 442 { 443 MessageBox(NULL, "Could not delete OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK); 444 return false; 445 } 446 glContext = null; 447 return true; 448 } 449 } 450 451 452 453 enum WGL_ARB_pixel_format= 1; 454 enum WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000; 455 enum WGL_DRAW_TO_WINDOW_ARB = 0x2001; 456 enum WGL_DRAW_TO_BITMAP_ARB = 0x2002; 457 enum WGL_ACCELERATION_ARB = 0x2003; 458 enum WGL_NEED_PALETTE_ARB = 0x2004; 459 enum WGL_NEED_SYSTEM_PALETTE_ARB = 0x2005; 460 enum WGL_SWAP_LAYER_BUFFERS_ARB = 0x2006; 461 enum WGL_SWAP_METHOD_ARB = 0x2007; 462 enum WGL_NUMBER_OVERLAYS_ARB = 0x2008; 463 enum WGL_NUMBER_UNDERLAYS_ARB = 0x2009; 464 enum WGL_TRANSPARENT_ARB = 0x200A; 465 enum WGL_TRANSPARENT_RED_VALUE_ARB = 0x2037; 466 enum WGL_TRANSPARENT_GREEN_VALUE_ARB = 0x2038; 467 enum WGL_TRANSPARENT_BLUE_VALUE_ARB = 0x2039; 468 enum WGL_TRANSPARENT_ALPHA_VALUE_ARB = 0x203A; 469 enum WGL_TRANSPARENT_INDEX_VALUE_ARB = 0x203B; 470 enum WGL_SHARE_DEPTH_ARB = 0x200C; 471 enum WGL_SHARE_STENCIL_ARB = 0x200D; 472 enum WGL_SHARE_ACCUM_ARB = 0x200E; 473 enum WGL_SUPPORT_GDI_ARB = 0x200F; 474 enum WGL_SUPPORT_OPENGL_ARB = 0x2010; 475 enum WGL_DOUBLE_BUFFER_ARB = 0x2011; 476 enum WGL_STEREO_ARB = 0x2012; 477 enum WGL_PIXEL_TYPE_ARB = 0x2013; 478 enum WGL_COLOR_BITS_ARB = 0x2014; 479 enum WGL_RED_BITS_ARB = 0x2015; 480 enum WGL_RED_SHIFT_ARB = 0x2016; 481 enum WGL_GREEN_BITS_ARB = 0x2017; 482 enum WGL_GREEN_SHIFT_ARB = 0x2018; 483 enum WGL_BLUE_BITS_ARB = 0x2019; 484 enum WGL_BLUE_SHIFT_ARB = 0x201A; 485 enum WGL_ALPHA_BITS_ARB = 0x201B; 486 enum WGL_ALPHA_SHIFT_ARB = 0x201C; 487 enum WGL_ACCUM_BITS_ARB = 0x201D; 488 enum WGL_ACCUM_RED_BITS_ARB = 0x201E; 489 enum WGL_ACCUM_GREEN_BITS_ARB = 0x201F; 490 enum WGL_ACCUM_BLUE_BITS_ARB = 0x2020; 491 enum WGL_ACCUM_ALPHA_BITS_ARB = 0x2021; 492 enum WGL_DEPTH_BITS_ARB = 0x2022; 493 enum WGL_STENCIL_BITS_ARB = 0x2023; 494 enum WGL_AUX_BUFFERS_ARB = 0x2024; 495 enum WGL_NO_ACCELERATION_ARB = 0x2025; 496 enum WGL_GENERIC_ACCELERATION_ARB = 0x2026; 497 enum WGL_FULL_ACCELERATION_ARB = 0x2027; 498 enum WGL_SWAP_EXCHANGE_ARB = 0x2028; 499 enum WGL_SWAP_COPY_ARB = 0x2029; 500 enum WGL_SWAP_UNDEFINED_ARB = 0x202A; 501 enum WGL_TYPE_RGBA_ARB = 0x202B; 502 enum WGL_TYPE_COLORINDEX_ARB = 0x202C; 503 504 505 enum WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; 506 enum WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; 507 enum WGL_CONTEXT_LAYER_PLANE_ARB = 0x2093; 508 enum WGL_CONTEXT_FLAGS_ARB = 0x2094; 509 enum WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; 510 511 512 enum WGL_CONTEXT_DEBUG_BIT_ARB = 0x0001; 513 enum WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002; 514 515 enum WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; 516 enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; 517 518 519 enum ERROR_INVALID_VERSION_ARB = 0x2095; 520 enum ERROR_INVALID_PROFILE_ARB = 0x2096;